﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
#if SILVERLIGHT
using jSLCore.Client.Presentation.Authorization;
#endif
using jSLCore.Client.Presentation.BaseControl;
using jSLCore.Client.Presentation.BaseControl.Advanced;
using jSLCore.Client.Presentation.Common;
using jSLCore.Client.Presentation.Controls;
using jSLCore.Client.Presentation.Util;
using jSLCore.Client.Utility;

namespace jSLCore.Client.Presentation.ApplicationContainer
{
    /// <summary>
    /// jSLCore container
    /// </summary>
    public partial class ApplicationContainer
    {
        private bool _useAnimation=true;
        public bool UseAnimation
        {
            get { return _useAnimation; }
            set { _useAnimation = value; }
        }

        /// <summary>
        /// All controls of type DatControl will use this worker to do async work
        /// </summary>
        internal SuperWorker SuperWorker;

        private readonly IMainFrame _contentContainer;
        /// <summary>
        /// Default constructor
        /// </summary>
        public ApplicationContainer(IMainFrame contentContainer)
        {
            if(contentContainer==null)throw new ArgumentNullException("contentContainer");
            _contentContainer = contentContainer;
            //wpf can if (Current != null) throw new InvalidOperationException("CurrentApplicationContainer нельзя создавать более одного раза");


            InitializeComponent();

            ConstructorCall();

            Resources.MergedDictionaries.Add(new ResourceDictionary
                                                 {Source = SLWPFSelector.CompileResourceString("CommonDictionary.xaml")});
            Resources.MergedDictionaries.Add(new ResourceDictionary
                                                 {
                                                     Source =
                                                         SLWPFSelector.CompileResourceString("DefaultVisualStyle.xaml")
                                                 });
            Resources.MergedDictionaries.Add(SLWPFSelector.LoadVisualResource());
            //for SL we load one resource, and for wpf another
            Resources.MergedDictionaries.Add(new ResourceDictionary
                                                 {
                                                     Source =
                                                         SLWPFSelector.CompileResourceString("ErrorToolTipTemplate.xaml")
                                                 });

#if !SILVERLIGHT
            Loaded += ApplicationSimpleLoaded;
#endif
            SuperWorker = new SuperWorker(this);
        }

        /// <summary>
        /// Get the ApplicationContainer associated with this ApplicationContainer
        /// </summary>
        public IMainFrame ContentContainer
        {
            get { return _contentContainer; }
        }


        /// <summary>
        /// Does hitting F5 will refresh the content of the current control
        /// </summary>
        public bool AllowF5 { get; set; }

        private UIElement MainContent
        {
// ReSharper disable UnusedMember.Local
            get { return (UIElement) mainContent.Content; }
// ReSharper restore UnusedMember.Local
            set { mainContent.Content = value; }
        }

        #region Dialog

        #region Показ диалога

        /// <summary>
        /// Showing dialog
        /// </summary>
        /// <param name="dialogControl">The dialog to be shown</param>
        /// <param name="dataSelectedEventHandler">To do if dialog returns single object</param>
        /// <param name="multiDataSelectedEventHandler">To do if dialog returns multiple objects</param>
        /// <param name="cancelHandler">To do if dialog was canceled by user or by program</param>
        /// <param name="allowCancel">If dialog can - it will be shown with cancel button. But dialog could override this value. You must provide cancel action, if the value is true</param>
        /// <param name="parentWindow">Parent window</param>
        /// <exception cref="ArgumentNullException">You must set allowCancel to true if you provide cancelHandler</exception>
        internal void ShowDialog(DialogControl dialogControl
                                 , DataSelectedEventHandler dataSelectedEventHandler
                                 , MultiDataSelectedEventHandler multiDataSelectedEventHandler
                                 , EventHandler cancelHandler
                                 , bool allowCancel,
                                 UIElement parentWindow)
        {
            if (!allowCancel && cancelHandler != null)
                throw new ArgumentNullException("allowCancel", "You provide cancelHandler, but allowCancel is false");

            #region Подписываем пользователя на события диалога

            if (cancelHandler != null) dialogControl.CancelSelected += cancelHandler;
            if (dataSelectedEventHandler != null) dialogControl.DataSelected += dataSelectedEventHandler;
            if (multiDataSelectedEventHandler != null) dialogControl.MultiDataSelected += multiDataSelectedEventHandler;

            #endregion

            dialogControl.AllowCancel = allowCancel;
            //Позволяем пользователю указать обязательность ввода, если это позволяет задавать сам диалог, надеемся, что он выкенет исключение в случае неправильного значения
            dialogControl.ParentWindow = parentWindow; //Указываем, кто его создал

            EnqueueDialog(dialogControl); //здесь он будет биндится к событиям

            #region Если из диалога кинулась отмена или ok, то закрываем этот диалог и все над ним лежащие(Те, которые он создал). 

            if (allowCancel)
                dialogControl.CancelSelected += (sender, args) =>
                                                CloseDialogAndChildren((DialogControl) sender);
            dialogControl.DataSelected += (sender, data) => CloseDialogAndChildren((DialogControl) sender);
            dialogControl.MultiDataSelected += (sender, data) => CloseDialogAndChildren((DialogControl) sender);

            #endregion
        }

        /// <summary>
        /// Закрыть диалог и все его дочерние контролы
        /// </summary>
        private void CloseDialogAndChildren(DialogControl parent)
        {
            //Если он наверху, то просто закрываем его
            if (Dialog == parent)
            {
                //Dialog.KillAllBindings();
                DequeueDialog();
            }
            else
            {
                IEnumerable<DialogControl> children = _dialogQueue.Where(d => d.ParentWindow == parent);
                //Ищем все диалоги, которые были созданы этим диалогом
                foreach (DialogControl child in children)
                {
                    UnbindDialog(child); //отвязываем каждый
                    CloseDialogAndChildren(child); //Закрываем каждый, но прежде делаем это с его потомками
                }


                //Далее происходит хак, потому что нам надо очистить стэк от ненужных диалогов
                var dialogList = _dialogQueue.ToArray().ToList();
                //parent.KillAllBindings();
                PopDialog(parent);
                dialogList.Remove(parent);

                _dialogQueue = new Stack<DialogControl>(dialogList);
            }
        }

        #endregion

        /// <summary>
        /// Стэк диалогов
        /// </summary>
        private Stack<DialogControl> _dialogQueue = new Stack<DialogControl>();

        internal
#if SILVERLIGHT
            UserControl 
#else
            Frame
#endif
            ActiveDialogPresenter
        {
            get
            {
                return 
#if SILVERLIGHT
                    (UserControl)
#else
                    (Frame)
#endif
                    (dialogGrid
                    .Children
                    .Cast<FrameworkElement>()
                    .FirstOrDefault(child => (child).Visibility == Visibility.Visible));
            }
        }

        /// <summary>
        /// Текущий диалог. !не находится в стэке, но мы предполагаем, что он в нашем расщиренном стэке
        /// </summary>
        public DialogControl Dialog
        {
            get
            {
                if (ActiveDialogPresenter == null) return null;

                return ActiveDialogPresenter.Content as DialogControl;
            }
        }

        /// <summary>
        /// Диалог нам указывает можно ли показывать OK кнопку
        /// </summary>
        private void SetOkEnabled(bool val)
        {
            okCancel.OkIsEnabled = val;
        }

        /// <summary>
        /// Открыть новый диалог. Этим методом мы расширяем стэк на один текущий диалог
        /// </summary>
        private void EnqueueDialog(DialogControl dialogControl)
        {
            if (Dialog != null)
            {
                UnbindDialog(Dialog);
                _dialogQueue.Push(Dialog);
            }

            BindDialog(dialogControl);

            if (dialogControl is DoWorkByDialog)
            {
                dialogContainer.Opacity = 0;
            }
            else
            {
                dialogContainer.Opacity = 100;
            }
            if (UseAnimation)
            {
                dialogFadeIn.Begin();
            }
            else
            {
                dialogContainer.Visibility = Visibility.Visible;
            }
        }

        /// <summary>
        /// Добавить новый диалог в DialogPresenter
        /// </summary>
        private void PushDialog(DialogControl dialogControl)
        {
            if (Dialog != null)
            {
                var activeDialogPresenter = ActiveDialogPresenter;

                activeDialogPresenter.Visibility = Visibility.Collapsed;
                activeDialogPresenter.IsEnabled = false;
            }
            else
            {
                DisableMainContent();
            }

#if SILVERLIGHT
            var cp = dialogControl.FindAncestor<UserControl>();
#else
            var cp = dialogControl.FindAncestor<Frame>();
#endif
            if (cp == null)
            {
#if SILVERLIGHT
                cp = new UserControl {Content = dialogControl};
#else
                cp = new Frame { Content = dialogControl };
#endif

                dialogGrid.Children.Add(cp);
            }
            else
            {
                cp.Visibility = Visibility.Visible;
                cp.IsEnabled = true;
            }
        }

        /// <summary>
        /// Убрать верхний диалог, и если есть ниже, то показать его, иначе закрыть все это дело
        /// </summary>
        private void DequeueDialog()
        {
            if (Dialog != null)
            {
                UnbindDialog(Dialog);

                PopDialog(Dialog);
            }

            if (_dialogQueue.Count > 0)
            {
                var dialog = _dialogQueue.Pop();

                BindDialog(dialog);
            }
            else
            {
                HideDialog();
            }
        }

        private void PopDialog(DialogControl dialog)
        {
            var cp = dialog.FindAncestor<ContentPresenter>();
            dialogGrid.Children.Remove(cp);
        }

        /// <summary>
        /// Спрятать контрол с диалогом
        /// </summary>
        private void HideDialog()
        {
            if (UseAnimation)
            {
                dialogFadeOut.Begin();
            }
            else
            {
                dialogContainer.Visibility = Visibility.Collapsed;
            }
            dialogGrid.Children.Clear();
            EnableMainContent();
        }

        private void EnableMainContent()
        {
            mainContent.IsEnabled = true;
        }

        private void DisableMainContent()
        {
            mainContent.IsEnabled = false;
        }

        /// <summary>
        /// Close all dialogs and show the current content
        /// </summary>
        public void CloseAllDialogs()
        {
            HideDialog();
        }

        /// <summary>
        /// Привязать указанный диалог к текущему окну
        /// </summary>
        private void BindDialog(DialogControl dialogControl)
        {
            okCancel.Width = dialogControl.Width; //this is need so dialog does not sctratch to Dialogborder size
            //okCancel.Height = 0;

            PushDialog(dialogControl);

            if (dialogControl.AutoSize)
            {
                dlgPlusHeader.HorizontalAlignment = HorizontalAlignment.Center;
                dlgPlusHeader.VerticalAlignment = VerticalAlignment.Center;
                dialogScroll.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
                dialogScroll.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
            }
            else
            {
                dlgPlusHeader.HorizontalAlignment = HorizontalAlignment.Stretch;
                dlgPlusHeader.VerticalAlignment = VerticalAlignment.Stretch;
                dialogScroll.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
                dialogScroll.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
            }

            if (_contentContainer != null)
            {
                if (!dialogControl.AutoSize)
                {
                    dlgPlusHeader.MaxWidth = Math.Min(_contentContainer.GetFrameworkElement().MaxWidth, dialogControl.MaxWidth);
                    dlgPlusHeader.MaxHeight = Math.Min(_contentContainer.GetFrameworkElement().MaxHeight, dialogControl.MaxHeight);
                }
                else
                {
                    dlgPlusHeader.MaxWidth = _contentContainer.GetFrameworkElement().MaxWidth;
                    dlgPlusHeader.MaxHeight = _contentContainer.GetFrameworkElement().MaxHeight;
                }
            }

            var dlgControl = dialogControl;

            okCancel.errorBox.BaseControl = dlgControl;
            if (dlgControl.Icon != null || !string.IsNullOrEmpty(dlgControl.Title))
            {
                dlgHeader.Image = dlgControl.Icon;
                dlgHeader.Header = dlgControl.Title;
                dlgHeader.Visibility = Visibility.Visible;
            }
            else
            {
                dlgHeader.Visibility = Visibility.Collapsed;
            }


            okCancel.Visibility = dialogControl.IsStandartOkCancelEnabled ? Visibility.Visible : Visibility.Collapsed;
            SetCancelIsVisible(dialogControl.AllowCancel);
            dialogControl.OkEnableChanged += SetOkEnabled;

            okCancel.OkIsEnabled = true; //Initializing... if no - Bind will do the thing
            dialogControl.CheckIsOkEnabled();
            okCancel.OkIsVisible = dialogControl.IsOkVisible;
            okCancel.BaseControl = dialogControl;
            okCancel.OkContent = dialogControl.OkContent;
            okCancel.CancelContent = dialogControl.CancelContent;
        }

        private void SetCancelIsVisible(bool value)
        {
            okCancel.CancelIsVisible = value;
            upperCloseButton.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
        }

        /// <summary>
        /// Отвязать диалог от сеанса
        /// </summary>
        private void UnbindDialog(IDialogControl dialogControl)
        {
            dialogControl.OkEnableChanged -= SetOkEnabled;
        }


        /// <summary>
        /// Закрытие диалога
        /// </summary>
        public void CloseDialog()
        {
            DequeueDialog();
        }

        private void dialogOk_Clicked(object sender, EventArgs e1)
        {
            if (Dialog != null) Dialog.OkClicked();
            else
            {
                throw new InvalidOperationException("Нажали ok, но не было диалога");
            }
        }

        /// <summary>
        /// В APplicationContainer нажади Cancel, уведомляем текущий диалог об этом, если он есть
        /// </summary>
        private void dialogCancel_Clicked(object sender, EventArgs e1)
        {
            CloseCurrentDialog();
        }

        private void CloseCurrentDialog()
        {
            if (Dialog != null) Dialog.CancelClicked();
            else
            {
                throw new InvalidOperationException("Нажали Cancel, но не было диалога");
            }
        }

        #endregion

        #region SplashScreen

        /// <summary>
        /// The splash screen implementer
        /// </summary>
        public ISplashScreen SplashScreen
        {
            get { return (ISplashScreen) splashScreen.Children[0]; }
            set
            {
                splashScreen.Children.Clear();
                splashScreen.Children.Add((UIElement) value);
            }
        }

        internal void ShowSplashScreen(object content)
        {
            Debug.WriteLine("Showing Splash Screen (Going to special Visual State)");
            if (content is string)
            {
                content = new TextBlock
                              {
                                  Text = (string) content,
                                  FontSize = 14,
                                  FontWeight = FontWeights.Bold,
                                  Margin = new Thickness(10)
                              };
            }
            SplashScreen.HeaderContent = content;
            if (SplashScreen is jSplashScreen)
                if(UseAnimation)
                VisualStateManager.GoToState(this, "SplashScreenVisible",UseAnimation);
            //SplashScreenIn.Begin();
            SplashScreen.IsBusy = true;
            /*loading.Start();
                loading.Visibility = Visibility.Visible;*/
        }

        internal void HideSplashScreen()
        {
            Debug.WriteLine("Hiding Splash Screen (Going to special Visual State)");
            SplashScreen.IsBusy = false;
            if (SplashScreen is jSplashScreen)
                if(UseAnimation)
                VisualStateManager.GoToState(this, "SplashScreenInvisible", UseAnimation);
        }


        private void splashScreenHide_Completed(object sender, EventArgs e)
        {
            SplashScreen.IsBusy = false;
        }

        /// <summary>
        /// Затемнение и просветление, все в одном
        /// </summary>
        public void Fade()
        {
            if (UseAnimation)
            {
                FadeInOut.Begin();
            }
            else
            {
                //FadeInOut.SkipToFill();
            }
        }

        #endregion

        partial void ConstructorCall();

        private void ApplicationSimpleLoaded(object sender, RoutedEventArgs e)
        {
            StartShowingForms();
        }

        partial void StartShowingForms();

        /// <summary>
        /// Показ по идее происходит только один раз. И перед первым разом происходит инициализация константной среды
        /// </summary>
        partial void ShowMainContentContainer();


        private void CheckAndSetMainContent()
        {
            var contentFrameworkElement = _contentContainer.GetFrameworkElement();
            MainContent = contentFrameworkElement;

            //Портируем веши из MainContentContainer
            Language = contentFrameworkElement.Language;
        }


        private void dialogContentSizeChanged(object sender, SizeChangedEventArgs e)
        {
            okCancel.Width = e.NewSize.Width;
            //this is need, cause error text can  set it bigger in autosize dialog mode
        }

        private void okCancel_Key(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Escape)
            {
                if (Dialog != null) Dialog.ProcessDefaultButtons(e);
            }
        }

        private void upperCloseDialog_Clicked(object sender, RoutedEventArgs e)
        {

            CloseCurrentDialog();
        }
    }
/*
    public class jSLCoreHost : ApplicationContainer
    {
        public jSLCoreHost(IMainFrame contentContainer)
            : base(contentContainer)
        {
        }

#if SILVERLIGHT

        public jSLCoreHost(bool isUserMustLogin, string authorizationServiceBaseAddress, ContentContainerBase contentContainer, AuthorizationControlBase authorizationControl=null) : base(isUserMustLogin, authorizationServiceBaseAddress, contentContainer,authorizationControl)
        {
        }
        public jSLCoreHost(bool isUserMustLogin, ISecurityManager securityManager, ContentContainerBase contentContainer, AuthorizationControlBase authorizationControl=null) : base(isUserMustLogin, securityManager, contentContainer, authorizationControl)
        {
        }

#endif

    }*/
}